// app.js - Express应用初始化
const express = require('express');
const cors = require('cors');
const path = require('path');
const morgan = require('morgan');
const dotenv = require('dotenv');

// 加载环境变量
dotenv.config({ path: path.resolve(__dirname, '../../.env') });

// 导入主路由
const mainRouter = require('./routes');

// 创建Express应用
const app = express();

// 中间件配置
app.use(morgan('dev')); // 日志
app.use(cors()); // 跨域支持
app.use(express.json()); // 自动解析 Content-Type: application/json 的请求体（JSON 数据），应对req.body
app.use(express.urlencoded({ extended: true })); // 自动解析 Content-Type: application/x-www-form-urlencoded 的请求体（表单数据）

// ======================
// 错误处理中间件定义,但是未注册，注册在后面，因为中间件的允许依赖于注册顺序，而这里的定义顺序并不符合实际需要
// ======================

// 1. JSON解析错误处理函数（路由前执行）
const handleJsonError = (err, req, res, next) => {
  // 处理req.body解析错误
  // 语法：检测SyntaxError类型错误
  // 作用：捕获JSON解析失败请求
  if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
    return res.status(400).json({ 
      success: false, 
      message: '无效的 JSON 格式' 
    });
  }
  next(err);
};

// 2. 全局错误处理函数（路由后执行）也即错误处理中间件
const handleGlobalError = (err, req, res, next) => {
  const statusCode = res.statusCode || 500; // 使用已设置的 statusCode 或默认 500
  res.status(statusCode).json({
    success: false,
    /**
     * 关于mongoose抛出的原生err,如果控制器中也直接捕获后抛出原生err，也即`throw err`是err解构如下：
     * "message": {
          "errors": {
            "type": {
              "stringValue": "\"string算法\"",
              "valueType": "string",
              "kind": "ObjectId",
              "value": "string算法",
              "path": "type",
              "reason": {},
              "name": "CastError",
              "message": "Cast to ObjectId failed for value \"string算法\" (type string) at path \"type\" because of \"BSONError\""
            }
          },
        "_message": "Question validation failed",
        "name": "ValidationError",
        "message": "Question validation failed: type: Cast to ObjectId failed for value \"string算法\" (type string) at path \"type\" because of \"BSONError\""
      },
     
      关于如果控制器是`throw new error()`则，这个err是个标准的error javascript对象,具体有什么是需要自己在new error() 定义：
      如果不定义那么输出就为
      {
        name:'Error', //由运行环境自动填充,默认为Error
        message: undefined,
        stack:'Error:undefined\n at ...' //由运行环境自动填充,JavaScript 引擎会根据当前代码的执行位置生成新的堆栈信息（stack）
      }
      但是console.log(err)是打印不出来的

      如果throw new Error("这是个错误")，那么message就被赋值为“这是个错误”
      一般来说name、stack是默认存在的由运行环境自行添加

      但是如果想修改name也可以(stack不能修改)：
      const err = new Error("这是个错误")
      err.name="xxx"

      throw err;
     */
    message: err.message,
    // 开发环境返回堆栈信息，生产环境忽略
    stack: process.env.NODE_ENV === 'production' ? null : err.stack
  });
  //console.log(`${statusCode},${process.env.NODE_ENV === 'production' ? null : err.stack}`);
};

// ======================
// 中间件注册（注意注册顺序决定执行顺序）
// ======================

// 请求体解析后立即注册JSON错误处理（路由前）
app.use(handleJsonError);

// API路由 - 统一使用主路由
app.use('/api', mainRouter);

// 静态文件服务 - 前端构建输出
app.use(express.static(path.join(__dirname, '../public')));

// API 404处理
app.use('/api/*', (req, res) => {
  res.status(404).json({ message: 'API endpoint not found' });
});

// 所有其他请求返回前端应用(SPA支持)
app.use('*', (req, res) => {
  res.sendFile(path.join(__dirname, '../public/index.html'));
});

// 最后注册全局错误处理（路由后）
app.use(handleGlobalError);

module.exports = app;
